home *** CD-ROM | disk | FTP | other *** search
- /*------------------------------------------------------------------------------
- Copyright (c) 2008 Ensolis, LLC. All Rights Reserved.
- ----------------------------------------------------------------------------*/
-
- /******************************************************************************
- * load a list of preferences into an object. Used in setting up items.
- *
- * @return An object hold preference names and value.
- ******************************************************************************/
- function getDefaultPrefs()
- {
- //set the return variable
- var rv = {};
- var sorter = [];
-
- //get the default pref branch and its children
- var branch = getBranch(true, null);
- var prefs = branch.getChildList("", {});
-
- //loop through prefs
- for (var i=0; i<prefs.length; i++) {
- var pref = prefs[i];
-
- //skip excluded
- if (EXCLUDED_PREFS.hasOwnProperty(pref) &&
- EXCLUDED_PREFS[pref] == true)
- continue;
-
- //get the preference value
- var value = "";
- try {
- switch (branch.getPrefType(pref)) {
- case Ci.nsIPrefBranch.PREF_INT:
- value = branch.getIntPref(pref);
- break;
- case Ci.nsIPrefBranch.PREF_BOOL:
- value = branch.getBoolPref(pref);
- break;
- case Ci.nsIPrefBranch.PREF_STRING:
- default:
- try {
- value = branch.getComplexValue(pref, Ci.nsIPrefLocalizedString).data;
- } catch(e) {
- value = branch.getCharPref(pref);
- }
- break;
- }
-
- //add to array
- sorter.push({ "name": pref, "value": value});
-
- //value does not exist in default so remove it
- } catch (e) {
- var userBranch = getBranch(false, null);
- if (userBranch.prefHasUserValue(pref))
- userBranch.clearUserPref(pref);
- }
- }
-
- // sort the array and add to object
- sorter.sort(sortByName);
- while (sorter.length) {
- var item = sorter.shift();
- rv[item.name] = item.value;
- }
-
- //return the variable
- return rv;
- }
-
- /******************************************************************************
- * Interface for describing the profile service. This service should
- * be used for all profile data related changes.
- * Migration, import, and export is handled by the migrator service.
- *
- * @status FROZEN
- * @version 1.0
- ******************************************************************************/
- function ProfileService()
- {
- //setup additional interfaces
- this._ifaces.push(Ci.nsIObserver);
- this._ifaces.push(Ci.nsISupportsWeakReference);
-
- //setup a new error
- this._error = Cc["@ensolis.com/forecastfox/error-item;1"].
- createInstance(Ci.ffIErrorItem);
- }
- ProfileService.prototype = {
- __proto__: new ServiceBase("ProfileService"),
- _obSvc: null,
- _items: null,
- _rotateTimer: null,
- _isBatch: null,
- _isRotating: null,
- _isLoading: null,
- _flushPending: null,
- _defaultPrefs: null,
-
- ///////////////////////////
- // nsIObserver
- observe: function ProfileService_observe(aSubject, aTopic, aData)
- {
- switch (aTopic) {
-
- //timer fired
- case "timer-callback":
-
- //rotating fired so switch profiles
- if (aSubject == this._rotateTimer) {
- this.current = this._nextItem();
- this._scheduleTimer();
- }
- break;
-
- //preference changed
- case "nsPref:changed":
-
- //ignore changes if we are loading
- if (this.isLoading)
- return;
-
- //current changed
- if (aData == "profile.current") {
- this.current = this.getItem(getPref(aData));
- return;
- }
-
- //keep properties in sync with prefs
- var id = this.current.ID;
- this._items[id].setProperty(aData, getPref(aData));
- this._flushData();
-
- //rotating prefs changed
- if (aData == "profile.switch.enabled" ||
- aData == "profile.switch.delay") {
-
- //start rotating if we are not in batch mode
- if (!this.isBatch)
- this.startRotating();
- return;
- }
- break;
- }
- },
-
- ////////////////////////////////
- // ffIService
-
- /**
- * Initialize the component. Called by the manager service. Returns false
- * if the datasource could not be loaded. Chech the lastError property for
- * more information.
- */
- start: function ProfileService_start()
- {
- //setup the variables
- this._items = {};
- this._isBatch = false;
- this._isRotating = false;
- this._isLoading = false;
- this._flushPending = false;
-
- //get the disk and migrator services
- var mgrSvc = Cc["@ensolis.com/forecastfox/manager-service;1"].
- getService(Ci.ffIManagerService);
- var migSvc = mgrSvc.migrator;
-
- //get the observer service
- this._obSvc = Cc["@mozilla.org/observer-service;1"].
- getService(Ci.nsIObserverService);
-
- //setup the timer with 10 minute repeat timer
- this._rotateTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
-
- //get the list of preference
- this._defaultPrefs = getDefaultPrefs();
-
- //start batch mode
- this.startBatch();
-
- //load profiles using the migration service
- var success = migSvc.migrate(this);
- if (!success) {
- var err = migSvc.lastError;
- this._error.init(err.severity, err.name, err.message);
- }
-
- //end batch mode
- this.endBatch();
-
- //setup a preference observer
- var pb2 = this.branch.QueryInterface(Ci.nsIPrefBranchInternal);
- pb2.addObserver("", this, true);
-
- //return our success code
- return success;
- },
-
- /**
- * Destroy the component. Called by the manager service. This may be
- * called prior to start so it needs to be safe.
- */
- stop: function ProfileService_stop()
- {
- //remove the pref observer
- var pb2 = this.branch.QueryInterface(Ci.nsIPrefBranchInternal);
- pb2.removeObserver("", this);
-
- //stop the rotating timer
- if (this._rotateTimer)
- this._rotateTimer.cancel();
-
- //clear variables
- this._obSvc = null;
- this._items = null;
- this._rotateTimer = null;
- this._flushTimer = null;
- this._isBatch = null;
- this._isRotating = null;
- this._isLoading = null;
- this._flushPending = null;
- this._defaultPrefs = null;
- },
-
- ////////////////////////////////
- // ffIProfileService
-
- /**
- * Currently selected profile item.
- */
- get current() { return this._ensureItem(); },
-
- set current(aVal) {
- //do nothing if null passed in
- if (!aVal)
- return;
-
- //do nothing if we don't have it
- if (!this.hasItem(aVal.ID))
- return;
-
- //load the items properties
- this._items[aVal.ID] = aVal.clone();
- this._loadItem(aVal);
-
- //notify observers
- this._obSvc.notifyObservers(aVal, "forecastfox-profiles", "current");
- },
-
- /**
- * Profile service is in batch mode.
- */
- get isBatch() { return this._isBatch; },
-
- /**
- * Profile service is in rotating mode.
- */
- get isRotating() { return this._isRotating; },
-
- /**
- * Profile is being loaded into preferences.
- */
- get isLoading() { return this._isLoading; },
-
- /**
- * Profiles has a specific item.
- *
- * @param ID of the profile item.
- * @return True if item exists.
- */
- hasItem: function ProfileService_hasItem(aID)
- {
- return this._items.hasOwnProperty(aID);
- },
-
- /**
- * Retrieve a profile item returns null if item doesn't exist.
- *
- * @param ID of the profile item.
- * @return A ffIProfileItem.
- */
- getItem: function ProfileService_getItem(aID)
- {
- if (!this.hasItem(aID))
- return null;
- else
- return this._items[aID];
- },
-
- /**
- * Retrieve an array of profile items.
- *
- * @param Count of items in the array.
- * @return An array of ffIProfileItem components.
- */
- getItems: function ProfileService_getItems(aCount)
- {
- //loop through items
- var items = [];
- for (var id in this._items)
- items.push(this._items[id]);
-
- //sort the array
- items.sort(sortByName);
-
- //return the array
- aCount.value = items.length;
- return items;
- },
-
- /**
- * Sets a profile item.
- *
- * @param The profile item.
- */
- setItem: function ProfileService_setItem(aItem)
- {
- //set the item
- this._items[aItem.ID] = aItem.clone();
-
- //if the item is current then load it
- if (!this.isBatch && this.current.ID == aItem.ID)
- this._loadItem(aItem);
-
- //flush async
- this._flushData();
-
- //notify observers
- this._obSvc.notifyObservers(this, "forecastfox-profiles", "setItem");
- },
-
- /**
- * Remove a profile item. It also removes that profiles data cache.
- *
- * @param ID of the profile item.
- */
- deleteItem: function ProfileService_deleteItem(aID)
- {
- //do nothing if we don't have item
- if (!this.hasItem(aID))
- return;
-
- //if the item is the current then load the next one
- if (!this.isBatch && this.current.ID == aID)
- this.current = this._nextItem();
-
- //remove the item
- delete this._items[aID];
-
- //remove the items cache
- var mgrSvc = Cc["@ensolis.com/forecastfox/manager-service;1"].
- getService(Ci.ffIManagerService);
- var dskSvc = mgrSvc.disk;
- var feed = dskSvc.get("feed-" + aID + ".js", TYPE_CACHE);
- var radar = dskSvc.get("radar-" + aID + ".gif", TYPE_CACHE);
- try {
- removeFile(feed);
- removeFile(radar);
- } catch(e) {}
-
- //flush async
- this._flushData();
-
- //notify observers
- this._obSvc.notifyObservers(this, "forecastfox-profiles", "deleteItem");
- },
-
- /**
- * Turn on batch mode. Observers will get a notification of this.
- * They will still get notified for every individual change, but they are
- * free to ignore those notifications.
- * Use this when a lot of changes are about to happen, and it would be
- * useless to refresh the display (or the backend store) for every change.
- * Caller must make sure to also call endBatch. Make sure all errors
- * are caught!
- */
- startBatch: function ProfileService_startBatch()
- {
- //do nothing if already batch
- if (this.isBatch)
- return;
-
- //stop rotating
- this.endRotating();
-
- //mark batch
- this._isBatch = true;
-
- //notify observers
- this._obSvc.notifyObservers(this, "forecastfox-profiles", "startBatch");
- },
-
- /**
- * Turn off batch mode.
- */
- endBatch: function ProfileService_endBatch()
- {
- //do nothing if not batch
- if (!this.isBatch)
- return;
-
- //make sure we have a current profile
- if (!this.hasItem(getPref("profile.current")))
- this.current = this._ensureItem();
- else
- this._loadItem(this.current);
-
- //mark not batch
- this._isBatch = false;
-
- // flush any pending changes
- if (this._flushPending) {
- this._flushData();
- this._flushPending = false;
- }
-
- //notify observers
- this._obSvc.notifyObservers(this, "forecastfox-profiles", "endBatch");
-
- //start rotating
- this.startRotating();
- },
-
- /**
- * Turn on rotating mode.
- */
- startRotating: function ProfileService_startRotating()
- {
- //do nothing if already rotating
- if (this.isRotating)
- return;
-
- //mark rotating
- this._isRotating = true;
-
- //start the timer
- this._scheduleTimer();
- },
-
- /**
- * Turn off rotating mode.
- */
- endRotating: function ProfileService_endRotating()
- {
- //do nothing if not rotating
- if (!this.isRotating)
- return;
-
- //mark not rotating
- this._isRotating = false;
-
- //stop the timer
- this._rotateTimer.cancel();
- },
-
- /**
- * Create a profile item from a DOM node.
- *
- * @param The profile DOM node.
- * @return A profile item.
- */
- createItem: function ProfileService__createItem(aNode)
- {
- //create an empty profile item
- var item = Cc["@ensolis.com/forecastfox/profile-item;1"].
- createInstance(Ci.ffIProfileItem);
-
- //set the name
- if (aNode.hasAttribute("name"))
- item.setProperty("name", aNode.getAttribute("name"));
- else
- item.setProperty("name", aNode.getAttribute("id"));
-
- //set the ID
- item.setProperty("ID", aNode.getAttribute("id"));
-
- //loop through pref nodes
- var nodes = aNode.getElementsByTagName("pref");
- for (var i=0; i<nodes.length; i++) {
- var node = nodes[i];
-
- //get the node attributes
- var name = node.getAttribute("name");
- var value = node.getAttribute("value");
- var type = node.getAttribute("type");
-
- //set the property based on type
- switch (type) {
- case "Int":
- item.setProperty(name, Number(value));
- break;
- case "Bool":
- item.setProperty(name, (value == "true"));
- break;
- case "Char":
- default:
- item.setProperty(name, value);
- break;
- }
- }
-
- //make sure any prefs not set are now
- for (var pref in this._defaultPrefs) {
- if (!item.hasProperty(pref))
- item.setProperty(pref, this._defaultPrefs[pref]);
- }
-
- //return the item
- return item;
- },
-
- /**
- * Create a DOM node from a profile item.
- *
- * @param The profile item.
- * @param The owner document of the node.
- * @return The DOM node.
- */
- createNode: function ProfileService_createNode(aItem, aDoc)
- {
- //create a profile node
- var profile = aDoc.createElementNS(PROFILES_NS, "profile");
-
- //set the name and id
- profile.setAttribute("id", aItem.ID);
- profile.setAttribute("name", aItem.name);
-
- //loop through the preferences
- for (var pref in this._defaultPrefs) {
-
- //get the preference value
- var value = "";
- if (aItem.hasProperty(pref))
- value = aItem.getProperty(pref);
- else
- value = this._defaultPrefs[pref];
-
- //set the node properties
- var node = aDoc.createElementNS(PROFILES_NS, "pref");
- node.setAttribute("name", pref);
- node.setAttribute("value", String(value));
- if (typeof value == "number")
- node.setAttribute("type", "Int");
- else if (typeof value == "boolean")
- node.setAttribute("type", "Bool");
- else
- node.setAttribute("type", "Char");
-
- //append it to the profile
- profile.appendChild(node);
- }
-
- //return the node
- return profile;
- },
-
- /**
- * Create a unique id for a profile item.
- *
- * @return A unique uuid.
- */
- createID: function ProfileService_createID()
- {
- //use the uuid generator if it exists
- var generator = Cc["@mozilla.org/uuid-generator;1"].
- getService(Ci.nsIUUIDGenerator);
-
- //create an id
- var id = generator.generateUUID().toString();
-
- //loop while the id is not unique
- while (this.hasItem(id))
- id = generator.generateUUID().toString();
-
- //return the id
- return id;
- },
-
- /**
- * Recreate an item from current preferences.
- *
- * @return A ffIProfileItem.
- */
- recreateItem: function ProfileService_recreateItem()
- {
- //create a new item
- var item = Cc["@ensolis.com/forecastfox/profile-item;1"].
- createInstance(Ci.ffIProfileItem);
-
- //loop through prefs and set the property
- for (var pref in this._defaultPrefs)
- item.setProperty(pref, getPref(pref));
-
- //set the name and id
- item.setProperty("ID", getPref("profile.current"));
- item.setProperty("name", item.getProperty("ID"));
-
- //return the item
- return item;
- },
-
- ////////////////////////////////
- // Internal Functions
-
- /**
- * Flush changes to disk.
- *
- * @param Call file writer in blocking mode.
- */
- _flushData: function ProfileService__flushData()
- {
- //do nothing if in batch mode
- if (this.isBatch) {
- this._flushPending = true;
- return;
- }
-
- //get the profiles file
- var mgrSvc = Cc["@ensolis.com/forecastfox/manager-service;1"].
- getService(Ci.ffIManagerService);
- var dskSvc = mgrSvc.disk;
- var file = dskSvc.get("profiles.xml", TYPE_PROFILE);
-
- //do nothing if not writable
- if ((!file.exists() && !file.parent.isWritable()) ||
- (file.exists() && !file.isWritable())) {
- dskSvc.log("Profile file is not writable: " + file.path, null, null);
- return;
- }
-
- //create a new profiles document
- var doc = dskSvc.create("profiles", PROFILES_DTD, PROFILES_NS);
-
- //loop through items
- for (var id in this._items) {
- var item = this._items[id];
-
- //create the node
- var node = this.createNode(item, doc);
-
- //append it to the document
- doc.documentElement.appendChild(node);
- }
-
- //write to disk
- dskSvc.write(file, doc, true, false);
- },
-
- /**
- * Schedule the rotation timer.
- */
- _scheduleTimer: function ProfileService__scheduleTimer()
- {
- //get the rotating prefs
- var enabled = getPref("profile.switch.enabled");
- var delay = getPref("profile.switch.delay");
- delay *= 60*1000;
-
- //not enabled or no delay
- if (!enabled || delay == 0)
- this.endRotating();
-
- //start the timer
- else
- this._rotateTimer.init(this, delay, Ci.nsITimer.TYPE_ONE_SHOT);
- },
-
- /**
- * Select the next profile item and schedule the timer.
- */
- _nextItem: function ProfileService__nextItem()
- {
- //setup variables
- var ids = [];
- var index = 0;
- var current = this.current.ID;
- var found = false;
-
- //loop through items
- for (var id in this._items) {
-
- //mark that we found the current
- if (current == id)
- found = true;
-
- //increment the index
- if (!found)
- index++;
-
- //save the id
- ids.push(id);
- }
-
- //return the next item
- index = (index + 1 < ids.length) ? index + 1 : 0;
- return this._items[ids[index]];
- },
-
- /**
- * Load an item into preferences.
- *
- * @param The item to load.
- */
- _loadItem: function ProfileService__loadItem(aItem)
- {
- //mark we are loading
- this._isLoading = true;
-
- //loop through the preferences
- for (var pref in this._defaultPrefs) {
-
- //set the preference if the item has it
- if (aItem.hasProperty(pref))
- setPref(pref, aItem.getProperty(pref));
-
- //use the default preference value
- else
- setPref(pref, this._defaultPrefs[pref]);
- }
-
- //set the current pref
- setPref("profile.current", aItem.ID);
-
- //mark we are finished loading
- this._isLoading = false;
- },
-
- /**
- * Ensure there is an item available for the current property.
- *
- * @return A profile item.
- */
- _ensureItem: function ProfileService__ensureItem()
- {
- //get the current item
- var item = this.getItem(getPref("profile.current"));
- if (!item) {
-
- //get all items
- var items = this.getItems({});
-
- //set to the first one
- if (items.length > 0)
- item = items[0];
-
- //recreate it from preferences
- else {
- item = this.recreateItem();
- this.setItem(item);
- }
- }
-
- //return the item
- return item;
- }
- };